--[[ scheme_type: generic author: Alundaio Scripted combat scheme that forces npc to be cautious and fight from cover. If a home_point is set then they will try to cover near here. --]] -------------------------------------- actid = 188118 evaid = 188118 -------------------------------------- local math_random = math.random ------------------------------------------------------------------------------------- -- PRIVATE -------------------------------------------------------------------------------------- local function validate(npc,vid) if (vid and vid < 4294967295 and npc:accessible(vid) and vid ~= npc:level_vertex_id()) then return db.used_level_vertex_ids[vid] == nil or db.used_level_vertex_ids[vid] == npc:id() end return false end local function lmove(npc,vid,st) if (vid == nil or vid >= 4294967295) then return end if (db.used_level_vertex_ids[vid] == npc:id()) then npc:set_dest_level_vertex_id(vid) return vid end if (st.vid) then db.used_level_vertex_ids[st.vid] = nil end if not (npc:accessible(vid)) then local vtemp = VEC_ZERO vid, vtemp = npc:accessible_nearest(vid and level.vertex_position(vid) or npc:position(), vtemp ) end db.used_level_vertex_ids[vid] = npc:id() st.vid = vid npc:set_dest_level_vertex_id(vid) return vid end local function lmove_ns(npc,vid,st) if (vid == nil or vid >= 4294967295) then return end if (db.used_level_vertex_ids[vid] == npc:id()) then npc:set_dest_level_vertex_id(vid) return vid end if (st.vid) then db.used_level_vertex_ids[st.vid] = nil end if not (npc:accessible(vid)) then local vtemp = VEC_ZERO vid, vtemp = npc:accessible_nearest(vid and level.vertex_position(vid) or npc:position(), vtemp ) end st.vid = vid npc:set_dest_level_vertex_id(vid) return vid end -------------------------------------------------------------------------------------- -- EVALUATOR FIGHT FROM COVER -------------------------------------------------------------------------------------- class "evaluator_fight_from_cover" (property_evaluator) function evaluator_fight_from_cover:__init(npc,name,storage) super (nil, name) self.st = storage self.st.stage = 0 end function evaluator_fight_from_cover:evaluate() --utils_data.debug_write("eva_fight_from_cover") local npc = self.object if not (npc:alive()) then return false end if (IsWounded(npc)) then return false end local st = db.storage[npc:id()] if (st) and (st.active_scheme == "camper" or st.rx_dont_shoot or st.help_wounded and st.help_wounded.selected_id ~= nil) then return false end local bd = npc:best_danger() if (bd and bd:type() == danger_object.grenade and st.danger_flag == true) then return false end if not (load_var(npc,"fight_from_point")) then -- or IsSniper(npc:best_weapon())) then return false end local be = npc:best_enemy() if (be and be:alive() and not IsWounded(be)) then return true end return false end -------------------------------------------------------------------------------------- -- ACTION FIGHT FROM COVER -------------------------------------------------------------------------------------- class "action_fight_from_cover" (action_base) function action_fight_from_cover:__init (npc,name,storage) super (nil,name) self.st = storage end function action_fight_from_cover:initialize() action_base.initialize(self) local npc = self.object npc:set_desired_position() npc:set_desired_direction() end function action_fight_from_cover:try_go_cover(npc,ene_pos) --utils_data.debug_write(strformat("action_fight_from_cover:try_go_cover")) local lvid = load_var(npc,"fight_from_point") local pos = lvid and level.vertex_position(lvid) or npc:position() local vid = utils_obj.find_close_cover(npc,pos,ene_pos,lvid and 1 or 5,1,lvid and 10 or 20) if (validate(npc,vid)) then return lmove(npc,vid,self.st) end end function action_fight_from_cover:try_go_backward(npc) --utils_data.debug_write(strformat("action_fight_from_cover:try_go_backward")) local dir = vector_rotate_y(vec_set(npc:direction()),math_random(160,200)) local vid local radius = 8 local base_point = npc:level_vertex_id() while (radius > 0) do vid = level.vertex_in_direction(base_point,dir,radius) if (vid and vid < 4294967295 and npc:accessible(vid) and vid ~= npc:level_vertex_id()) then return lmove_ns(npc,vid,self.st) end radius = radius - 1 end end function action_fight_from_cover:try_to_strafe(npc) --utils_obj.debug_nearest(npc,"try_to_strafe") local dir = {} dir[1] = vector_rotate_y(vec_set(npc:direction()),math.random(-87,-93)) dir[2] = vector_rotate_y(vec_set(npc:direction()),math.random(87,93)) local vid local radius local base_point = npc:level_vertex_id() for i=1,2 do radius = 4 while (radius > 1) do vid = level.vertex_in_direction(base_point,dir[i],radius) if (vid and vid < 4294967295 and npc:accessible(vid) and vid ~= npc:level_vertex_id()) then return lmove_ns(npc,vid,self.st) end radius = radius - 1 end end end function action_fight_from_cover:execute() --utils_data.debug_write(strformat("action_fight_from_cover:execute start")) action_base.execute(self) local npc = self.object local enemy = npc:best_enemy() if not (enemy) then return end -- ensure and enforce path type if (npc:path_type() ~= game_object.level_path) then npc:set_path_type(game_object.level_path) end local see = npc:see(enemy) --local see_me = enemy:see(npc) local ene_pos = see and utils_obj.safe_bone_pos(enemy,"bip01_neck") or npc:memory_position(enemy) or nil local tg = time_global() if (self.st.vid) then local bw = npc:active_item() and npc:best_weapon() if (self.st.__keep_body_state_until==nil or tg > self.st.__keep_body_state_until) then local dir = ene_pos and vec_sub(ene_pos,npc:position()) or npc:direction() self.st.__keep_body_state_until = tg+5000 if (npc:body_state() ~= move.crouch) then --printf("stand=%s",level.high_cover_in_direction(npc:level_vertex_id(),dir)) if (level.high_cover_in_direction(npc:level_vertex_id(),dir) < 0.2) then self.stand = false end else --printf("crouch=%s",level.high_cover_in_direction(npc:level_vertex_id(),dir)) if (level.high_cover_in_direction(npc:level_vertex_id(),dir) >= 0.2) then self.stand = true end end end local new_state if (npc:level_vertex_id() == self.st.vid) then if not (bw) then new_state = "sneak_run_no_wpn" else local is_sniper = IsSniper(bw) if (is_sniper == nil and self.stand) then new_state = see and (is_sniper and "threat_sniper_fire" or "threat_fire") or "threat" else new_state = see and (is_sniper and "hide_sniper_fire" or "sneak_fire") or "sneak_run" end end if (see) then local dir = ene_pos and vec_sub(ene_pos, utils_obj.safe_bone_pos(npc,"bip01_r_finger02")) or nil state_mgr.set_state(npc,new_state,nil,nil,{look_object = enemy},{fast_set = true}) else state_mgr.set_state(npc,new_state,nil,nil,{look_position = ene_pos},{fast_set = true}) end self.st.__keep_point_until = self.st.__keep_point_until or tg + math.random(3000,8000) if (self.st.__keep_point_until and tg < self.st.__keep_point_until) then return end self.st.__keep_point_until = nil db.used_level_vertex_ids[self.st.vid] = nil self.st.vid = nil else if not (bw) then new_state = "sneak_run_no_wpn" else local dist = npc:position():distance_to_sqr(enemy:position()) if (dist < 400) then local is_sniper = IsSniper(bw) if (self.stand) then new_state = see and (is_sniper and "threat_sniper_fire" or "assault_fire") or "assault" else new_state = see and (is_sniper and "hide_sniper_fire" or "sneak_fire") or "sneak_run" end else if (self.stand) then new_state = "assault" else new_state = "sneak_run" end end end if (see) then local dir = ene_pos and vec_sub(ene_pos,utils_obj.safe_bone_pos(npc,"bip01_r_finger02")) or nil state_mgr.set_state(npc,new_state,nil,nil,{look_object = enemy},{fast_set = true}) else state_mgr.set_state(npc,new_state,nil,nil,{look_position = ene_pos},{fast_set = true}) end lmove(npc,self.st.vid,self.st) return end end if (self:try_go_cover(npc,ene_pos or enemy:position())) then else self:try_to_strafe(npc) end --utils_data.debug_write(strformat("action_fight_from_cover:execute end 2")) end function action_fight_from_cover:finalize() action_base.finalize(self) if (self.st.vid) then db.used_level_vertex_ids[self.st.vid] = nil end self.st.__keep_target_until = nil end -------------------------------------------------------------------- --------------------------------- BINDER --------------------------- function setup_generic_scheme(npc,ini,scheme,section,stype,temp) local st = xr_logic.assign_storage_and_bind(npc,ini,scheme,section,temp) end function add_to_binder(npc,ini,scheme,section,storage,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager then return end if (not npc:alive()) or (npc:character_community() == "zombied" or npc:section() == "actor_visual_stalker") then manager:add_evaluator(evaid,property_evaluator_const(false)) temp.needs_configured = false return end local evaluator = evaluator_fight_from_cover(npc,"eva_fight_from_cover",storage) temp.action = action_fight_from_cover(npc,"act_fight_from_cover",storage) if not evaluator or not temp.action then return end manager:add_evaluator(evaid,evaluator) temp.action:add_precondition(world_property(stalker_ids.property_alive,true)) temp.action:add_precondition(world_property(stalker_ids.property_danger, false)) temp.action:add_precondition(world_property(evaid,true)) temp.action:add_effect(world_property(evaid,false)) manager:add_action(actid,temp.action) --xr_logic.subscribe_action_for_events(npc, storage, temp.action) end function configure_actions(npc,ini,scheme,section,stype,temp) if not npc then return end local manager = npc:motivation_action_manager() if not manager or not temp.action then return end --temp.action:add_precondition(world_property(xr_evaluators_id.state_mgr + 1,true)) temp.action:add_precondition(world_property(xr_evaluators_id.sidor_wounded_base,false)) temp.action:add_precondition(world_property(xr_evaluators_id.wounded_exist,false)) if (_G.schemes["rx_ff"]) then temp.action:add_precondition(world_property(rx_ff.evaid,false)) end if (_G.schemes["gl"]) then temp.action:add_precondition(world_property(rx_gl.evid_gl_reload,false)) end if (_G.schemes["facer"]) then temp.action:add_precondition(world_property(xrs_facer.evid_facer,false)) temp.action:add_precondition(world_property(xrs_facer.evid_steal_up_facer,false)) end local action local p = { xr_danger.actid, stalker_ids.action_combat_planner, stalker_ids.action_danger_planner, --xr_actions_id.state_mgr + 1, xr_actions_id.state_mgr + 2, xr_actions_id.alife } for i=1,#p do --printf("ACTION_ALIFE_ID(axr_fight_from_cover.configure_actions): " .. tostring(p[i])) action = manager:action(p[i]) if (action) then action:add_precondition(world_property(evaid,false)) else printf("axr_fight_from_cover: no action id p[%s]",i) end end end function disable_generic_scheme(npc,scheme,stype) local st = db.storage[npc:id()][scheme] if st then st.enabled = false end end function npc_add_precondition(action) if not action then return end action:add_precondition(world_property(evaid,false)) end